import { Injectable, Inject } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { Observable, of, zip, throwError } from 'rxjs'
import { catchError, map, mergeMap } from 'rxjs/operators'

import { SERVER_URL, COMB_URL } from 'app/utils/contraints'
import { UserService } from './user.service'
import {
  IUserQueryParams,
  IUserPagination,
  User,
  UserRole,
  IUpdateUser,
  IChangePassword,
  ICreateUser,
  ICreateUserForm,
} from '@shared/interfaces/user'
import { preProcessParams } from '@shared/decorators'
import { generateOptions } from 'app/utils/helpers'
import { ModelMapper } from '@shared/decorators/property-map'
import { IResponse, IRoleResponse } from '@shared/interfaces/response'
import { ERoleType } from '@shared/enums'
import { DA_SERVICE_TOKEN, ITokenService, JWTTokenModel } from '@delon/auth'

@Injectable()
export class UserAlinterService implements UserService {
  constructor(
    private http: HttpClient,
    @Inject(DA_SERVICE_TOKEN) private tokenService: ITokenService,
  ) {}

  @preProcessParams()
  getList(params: IUserQueryParams): Observable<IUserPagination> {
    const options = generateOptions(params)
    const url = `${COMB_URL}/camel/users`
    return this.http.get<IUserPagination>(url, options).pipe(catchError(err => throwError(err)))
  }

  update(username: string, params: IUpdateUser): Observable<User> {
    const url = `${COMB_URL}/camel/user/${username}`
    return this.http.put<User>(url, params).pipe(
      catchError(err => {
        return throwError(err)
      }),
    )
  }

  changePassword(username: string, params: IChangePassword): Observable<User> {
    const url = `${COMB_URL}/camel/user/${username}/password`
    return this.http.put<User>(url, params).pipe(
      catchError(err => {
        return throwError(err)
      }),
    )
  }

  changeMePassword(params: IChangePassword): Observable<boolean> {
    const url = `${COMB_URL}/camel/auth/password`
    return this.http.put<boolean>(url, params).pipe(
      catchError(err => {
        return throwError(err)
      }),
    )
  }

  getOne(userName: string): Observable<User> {
    // TODO: Still old api structure even calling new api endpoint.
    const url = `${COMB_URL}/camel/user/${userName}`
    /**
     * Pending backend to adding 'version' to this api.
     * Once the api is ready. Use 'version' in update user detail.
     */
    return this.http.get<User>(url).pipe(
      catchError(err => {
        return throwError(err)
      }),
    )
  }

  current(): Observable<User> {
    const url = `${COMB_URL}/auth/session`
    const jwtModel = this.tokenService.get<JWTTokenModel>(JWTTokenModel)
    return this.http.get<User>(url, {
      headers: {
        Authorization: `Bearer ${jwtModel.token}`,
      },
    }).pipe(
      catchError(err => {
        return throwError(err)
      }),
    )
  }

  create(user: ICreateUserForm): Observable<string> {
    const url = `${COMB_URL}/camel/user`
    return this.http.post<string>(url, user, {
      headers: {
        'X-CIL-API-Visa': 'EMDYCd6gCVgP',
      },
    })
  }

  delete(username: string): Observable<boolean> {
    const url = `${COMB_URL}/camel/user/${username}`
    return this.http.delete<boolean>(url).pipe(
      catchError(err => {
        return throwError(err)
      }),
    )
  }

  freeze(username: string): Observable<boolean> {
    const url = `${COMB_URL}/camel/user/${username}/freeze`
    return this.http.put<boolean>(url, {}).pipe(
      catchError(err => {
        return throwError(err)
      }),
    )
  }

  unfreeze(username: string): Observable<boolean> {
    const url = `${COMB_URL}/camel/user/${username}/unfreeze`
    return this.http.put<boolean>(url, {}).pipe(
      catchError(err => {
        return throwError(err)
      }),
    )
  }

  getAcquirerRole(insCode: string, type: ERoleType): Observable<UserRole[]> {
    // const url = `/master/institution/roles?insCode=${insCode}&types=${type}`
    const url = `${COMB_URL}/camel/institution/${insCode}/roles`
    return this.http
      .get<IRoleResponse>(url, {
        withCredentials: true,
      })
      .pipe(
        map(resp => {
          const numType = parseInt(`${type}`, 10)
          switch (numType) {
            case ERoleType.Acquirer: {
              return resp.insRoles
            }
            case ERoleType.Group: {
              return resp.agentRoles
            }
            case ERoleType.Merchant: {
              return resp.merchantRoles
            }
            case ERoleType.Store: {
              return resp.storeRoles
            }
            default: {
              return []
            }
          }
        }),
        catchError(err => {
          console.log('error is ', err)
          return of(false)
        }),
      )
  }
}
